/******************************************************************************
 * Copyright 2022 The AIR Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#pragma once

#include <cassert>
#include <iostream>
#include <memory>
#include <string>

#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>

namespace cw {

class KeyPair {
 public:
  /**
   * RSA 公钥指数
   */
  static constexpr auto kRSAPublicExponent = 0x10001;
  /**
   * RSA Bit Length 最小值
   */
  static constexpr auto kRSAMinBitLength = 2048;
  /**
   * RSA Bit Length 最大值
   */
  static constexpr auto kRSAMaxBitLength = 16384;

  struct tag_rsa_t {
    /**
     * RSA key length:
     * Valid
     */
    int bit_length;
  };
  struct tag_ec_t {
    /**
     * curve NID
     * For example:
     *     NID_secp224r1
     *     NID_secp256k1
     *     NID_secp384r1
     *     NID_secp521r1
     *     NID_X9_62_prime256v1
     */
    int curve;
  };
  struct tag_public_t {};

  /**
   * Load Key From File
   * @param filename
   */
  explicit KeyPair(const std::string &filename,
                   const std::string &password = "");
  explicit KeyPair(const std::string &filename, tag_public_t);

  /**
   * Load Key From Buffer
   * @param filename
   */
  explicit KeyPair(const void *buffer, int buflen,
                   const std::string &password = "");
  explicit KeyPair(const void *buffer, int buflen, tag_public_t);

  /**
   * Create RSA Key
   * @param tag
   */
  explicit KeyPair(tag_rsa_t tag);
  /**
   * Create EC Key
   * @param tag
   */
  explicit KeyPair(tag_ec_t tag);

  /**
   * Load from other key
   */
  explicit KeyPair(RSA *rsa);
  explicit KeyPair(EC_KEY *ec_key);

  operator bool() const {  // NOLINT
    return pkey_ != nullptr;
  }

  // =========================================================================
  KeyPair() = default;
  virtual ~KeyPair() = default;
  KeyPair(const KeyPair &) = default;
  KeyPair &operator=(const KeyPair &) = default;
  KeyPair(KeyPair &&) = default;
  KeyPair &operator=(KeyPair &&) = default;
  // =========================================================================
  bool IsPublic() const;
  const EVP_PKEY *NativeHandle() const;
  int Id() const;
  int BaseId() const;

  std::string PemString(const std::string &password = "") const;
  std::string PemString(tag_public_t) const;

 private:
  // =========================================================================
  static bool Check(EVP_PKEY *pkey, bool is_public = false);
  // =========================================================================
  std::shared_ptr<EVP_PKEY> pkey_{nullptr};
  bool is_public_{false};
  explicit KeyPair(BIO *bio, const std::string &password = "");
  KeyPair(BIO *bio, tag_public_t);
};

}  // namespace cw
#include "impl/key-pair-impl.hpp"
